package com.novel.disk.common;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;


public class ImageUtils {

    public static final String FORMAT_NAME = "jpg";

    /**
     * 计算缩放比
     */
    private static int getScale(Long size) {
        Long defaultSize = 1024 * 1024L;
        int scale = 1;
        if (size > defaultSize) {
            long a = size / defaultSize;
            scale = Math.round(a);
        }
        return scale;
    }

    /**
     * 翻转图片
     *
     * @param src
     * @param angel
     * @return
     */
    public static BufferedImage rotateImage(Image src, int angel) {
        int src_width = src.getWidth(null);
        int src_height = src.getHeight(null);
        // calculate the new image size
        Rectangle rect_des = CalcRotatedSize(new Rectangle(new Dimension(src_width, src_height)), angel);
        BufferedImage res = null;
        res = new BufferedImage(rect_des.width, rect_des.height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = res.createGraphics();
        // transform
        g2.translate((rect_des.width - src_width) / 2, (rect_des.height - src_height) / 2);
        int width = src_width / 2;
        int height = src_height / 2;
        g2.rotate(Math.toRadians(angel), width, height);

        g2.drawImage(src, null, null);
        g2.dispose();
        return res;
    }

    /**
     * 计算旋转参数
     */
    public static Rectangle CalcRotatedSize(Rectangle src, int angel) {
        // if angel is greater than 90 degree,we need to do some conversion.
        if (angel > 90) {
            if (angel / 9 % 2 == 1) {
                src.height = src.height ^ src.width;
                src.width = src.height ^ src.width;
                src.height = src.height ^ src.width;
            }
            angel = angel % 90;
        }

        double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2;
        double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
        double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2;
        double angel_dalta_width = Math.atan((double) src.height / src.width);
        double angel_dalta_height = Math.atan((double) src.width / src.height);

        int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_width));
        int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_height));
        int des_width = src.width + len_dalta_width * 2;
        int des_height = src.height + len_dalta_height * 2;
        return new Rectangle(new Dimension(des_width, des_height));
    }


    /**
     * 图片缩放
     *
     * @param in        源数据
     * @param width     宽
     * @param height    高
     * @param isFilling 比例不对时是否需要补白 isFilling true 补白
     * @return 缩放后的数据
     */
    public static byte[] resize(byte[] in, int width, int height, boolean isFilling) {
        try {
            BufferedImage bi = ImageIO.read(FileUtils.byte2Input(in));
            Image image = getImage(width, height, isFilling, bi);
            BufferedImage bufferedImage = toBufferedImage(image);
            return bufferedImage2bytes(bufferedImage);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * BufferedImage转byte
     *
     * @param bufferedImage
     * @return
     * @throws IOException
     */
    public static byte[] bufferedImage2bytes(BufferedImage bufferedImage) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, FORMAT_NAME, os);
        return os.toByteArray();
    }

    private static Image getImage(int width, int height, boolean isFilling, BufferedImage bi) {
        double ratio = 0.0; // 缩放比例
        Image itemp = bi.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        // 计算比例
        if ((bi.getHeight() > height) || (bi.getWidth() > width)) {
            if (bi.getHeight() > bi.getWidth()) {
                ratio = (new Integer(height)).doubleValue() / bi.getHeight();
            } else {
                ratio = (new Integer(width)).doubleValue() / bi.getWidth();
            }
            AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
            itemp = op.filter(bi, null);
        }
        if (isFilling) {// 补白
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.setColor(Color.white);
            g.fillRect(0, 0, width, height);
            if (width == itemp.getWidth(null)) {
                g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2, itemp.getWidth(null),
                        itemp.getHeight(null), Color.white, null);
            } else {
                g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0, itemp.getWidth(null),
                        itemp.getHeight(null), Color.white, null);
            }
            g.dispose();
            itemp = image;
        }
        return itemp;
    }

    /**
     * 给图片添加文字水印
     *
     * @param pressText     水印文字
     * @param in            源图像地址
     * @param destImageFile 目标图像地址
     * @param fontName      水印的字体名称
     * @param fontStyle     水印的字体样式
     * @param color         水印的字体颜色
     * @param fontSize      水印的字体大小
     * @param x             修正值
     * @param y             修正值
     * @param alpha         透明度：alpha 必须是范围 [0.0, 1.0] 之内（包含边界值）的一个浮点数字
     */
    public static void pressText(String pressText, InputStream in, String destImageFile, String fontName,
                                 int fontStyle, Color color, int fontSize, int x, int y, float alpha) {
        try {
            Image src = ImageIO.read(in);
            int width = src.getWidth(null);
            int height = src.getHeight(null);
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            g.drawImage(src, 0, 0, width, height, null);
            g.setColor(color);
            g.setFont(new Font(fontName, fontStyle, fontSize));
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
            // 在指定坐标绘制水印文字
            g.drawString(pressText, (width - (getLength(pressText) * fontSize)) / 2 + x, (height - fontSize) / 2 + y);
            g.dispose();
            ImageIO.write(toBufferedImage(image), FORMAT_NAME, new File(destImageFile));// 输出到文件流
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 裁剪图片(裁剪为正方形)
     **/
    public static void resize(InputStream in, File target, int max) throws Exception {
        InputStream input = new BufferedInputStream(in);
        OutputStream output = new BufferedOutputStream(new FileOutputStream(target));
        BufferedImage image = ImageIO.read(input);
        crop(image, output, max, max, StringUtils.equalsIgnoreCase("png", FilenameUtils.getExtension(target.getName())));
    }

    /**
     * 裁剪图片(裁剪为正方形)
     **/
    public static void resize(byte[] in, File target, int max) throws Exception {
        ByteArrayInputStream bi = new ByteArrayInputStream(in); // 将b作为输入流；
        OutputStream output = new BufferedOutputStream(new FileOutputStream(target));
        BufferedImage image = ImageIO.read(bi); // 将in作为输入流，读取图片存入image中，而这里in可以为ByteArrayInputStream();
        crop(image, output, max, max, false);
    }

    public static void crop(BufferedImage buffer, OutputStream output, int maxWidth, int maxHeight, boolean isPNG)
            throws Exception {
        try {
            int tmpWidth = buffer.getWidth();
            int tmpHeight = buffer.getHeight();
            int toWidth = tmpWidth;
            int toHeight = tmpHeight;
            if (toWidth > maxWidth && toHeight > maxHeight) {
                double rate = new BigDecimal(toWidth).divide(new BigDecimal(toHeight), 2, BigDecimal.ROUND_HALF_UP)
                        .doubleValue();
                if (maxWidth > 0) {
                    toWidth = maxWidth;
                    toHeight = (int) (toWidth / rate);
                }
                if (maxHeight > 0 && toHeight > maxHeight) {
                    toHeight = maxHeight;
                    toWidth = (int) (toHeight * rate);
                }
                BufferedImage tmpBuffer = new BufferedImage(toWidth, toHeight,
                        isPNG ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
                Graphics graphics = tmpBuffer.getGraphics();
                graphics.drawImage(buffer.getScaledInstance(toWidth, toHeight, Image.SCALE_SMOOTH), 0, 0,
                        null);
                graphics.dispose();
                buffer = tmpBuffer;
            }
            BufferedImage dest = null;
            if (toWidth > toHeight) {
                dest = buffer.getSubimage((toWidth - toHeight) / 2, 0, toHeight, toHeight);
            } else {
                dest = buffer.getSubimage(0, (toHeight - toWidth) / 2, toWidth, toWidth);
            }
            BufferedImage tag = new BufferedImage(Math.min(toWidth, toHeight), Math.min(toWidth, toHeight),
                    isPNG ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
            Graphics graphics = tag.getGraphics();
            graphics.drawImage(dest, 0, 0, null);
            graphics.dispose();
            ImageIO.write(tag, isPNG ? "png" : FORMAT_NAME, output);
            // Image.SCALE_SMOOTH 的缩略算法 生成缩略图片的平滑度的 优先级比速度高 生成的图片质量比较好 但速度慢
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (output != null) {
                output.close();
            }
        }
    }

    /**
     * 计算text的长度（一个中文算两个字符）
     *
     * @param text
     * @return
     */
    public static int getLength(String text) {
        int length = 0;
        for (int i = 0; i < text.length(); i++) {
            if ((text.charAt(i) + "").getBytes().length > 1) {
                length += 2;
            } else {
                length += 1;
            }
        }
        return length / 2;
    }

    /**
     * 图像类型转换：GIF->JPG、GIF->PNG、PNG->JPG、PNG->GIF(X)、BMP->PNG
     *
     * @param srcImageFile  源图像地址
     * @param formatName    包含格式非正式名称的 String：如JPG、JPEG、GIF等
     * @param destImageFile 目标图像地址
     */
    public static void convert(String srcImageFile, String formatName, String destImageFile) {
        try {
            File f = new File(srcImageFile);
            BufferedImage src = ImageIO.read(f);
            ImageIO.write(src, formatName, new File(destImageFile));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static BufferedImage toBufferedImage(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage) image;
        }
        image = new ImageIcon(image).getImage();
        BufferedImage bimage = null;
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        try {
            int transparency = Transparency.OPAQUE;
            GraphicsDevice gs = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gs.getDefaultConfiguration();
            bimage = gc.createCompatibleImage(image.getWidth(null), image.getHeight(null), transparency);
        } catch (HeadlessException e) {
            // The system does not have a screen
        }
        if (bimage == null) {
            // Create a buffered image using the default color model
            int type = BufferedImage.TYPE_INT_RGB;
            // int type = BufferedImage.TYPE_3BYTE_BGR;//by wang
            /*
             * if (hasAlpha) { type = BufferedImage.TYPE_INT_ARGB; }
             */
            bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
        }

        // Copy image to buffered image
        Graphics g = bimage.createGraphics();
        // Paint the image onto the buffered image
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return bimage;
    }

    /**
     * 将{@link BufferedImage}生成formatName指定格式的图像数据
     *
     * @param source
     * @param formatName 图像格式名，图像格式名错误则抛出异常
     * @return
     */
    public static byte[] writeBytes(BufferedImage source, String formatName) {
        Assert.notNull(source, "source is null");
        Assert.notNull(formatName, "formatName is empty");
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        Graphics2D g = null;
        try {
            for (BufferedImage s = source; !ImageIO.write(s, formatName, output); ) {
                if (null != g) {
                    throw new IllegalArgumentException(String.format("not found writer for '%s'", formatName));
                }
                s = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_RGB);
                g = s.createGraphics();
                g.drawImage(source, 0, 0, null);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (null != g) {
                g.dispose();
            }
        }
        return output.toByteArray();
    }


    /**
     * 自动调节精度(经验数值)
     *
     * @param size 源图片大小
     * @return 图片压缩质量比
     */
    private static double getAccuracy(long size) {
        double accuracy;
        if (size < 900) {
            accuracy = 0.85;
        } else if (size < 2047) {
            accuracy = 0.6;
        } else if (size < 3275) {
            accuracy = 0.44;
        } else if (size < 4096) {
            accuracy = 0.3;
        } else {
            accuracy = 0.2;
        }
        return accuracy;
    }


    /**
     * 读取文件，并将它转化为BufferedImage
     *
     * @param path 文件路径
     * @return BufferedImage
     * @throws IOException IOException
     */
    public static BufferedImage readImg(String path) throws IOException {
        File img = new File(path);
        if (!img.exists()) {
            return null;
        }
        return ImageIO.read(img);
    }

    /**
     * 将图片文件转换为 BufferedImage
     *
     * @param file 要转化的图片文件
     * @return BufferedImage
     * @throws IOException IOException
     */
    public static BufferedImage readImg(File file) throws IOException {
        if (!file.exists()) {
            return null;
        }
        return ImageIO.read(file);
    }

    public static BufferedImage readImg(InputStream inputStream) throws IOException {
        return ImageIO.read(inputStream);
    }
}
